#include "io.hpp"

extern std::map<uint64_t, Semaphore<std::array<char, 4096>>> memory;
static int portAvail = 2; // 允许分配的最小端口号，遵循PID分配方式

void SerialIn::read() { value = getchar(); }
void SerialIn::write() { setStat(BAD); }
void SerialIn::set(uint64_t) { setStat(BAD); }
uint64_t SerialIn::get() { return value; }
void SerialIn::run(Semaphore<std::pair<uint16_t, uint16_t>> &lock)
{
	lock.w_lock();
	setStat(BUSY);
	read();
	setStat(DONE);
	lock.get().second = 0x90;
	lock.w_unlock();
}
void SerialOut::read() { setStat(BAD); }
void SerialOut::write() { putchar(value); }
void SerialOut::set(uint64_t _value) { value = _value & 0xff; }
uint64_t SerialOut::get() { return value; }
void SerialOut::run(Semaphore<std::pair<uint16_t, uint16_t>> &lock)
{
	lock.w_lock();
	write();
	setStat(DONE);
	lock.get().second = 0x90;
	lock.w_unlock();
}
void MemoryIn::read()
{
	uint16_t offset = maddr & 0xfff;
	if (offset + count > 4096)
	{
		setStat(BAD);
		return;
	}
	memory[maddr >> 12].w_lock();
	for (int i = 0; i < count; i++)
		memory[maddr >> 12].get()[offset + i] = getchar();
	memory[maddr >> 12].w_unlock();
	setStat(DONE);
}
void MemoryIn::write() { setStat(BAD); }
void MemoryIn::set(uint64_t _value)
{
	switch (getStat())
	{
	case SET0:
		maddr = _value;
		break;
	case SET1:
		count = _value & 0xfff;
		break;
	default:
		break;
	}
}
uint64_t MemoryIn::get()
{
	switch (getStat())
	{
	case GET0:
		return maddr;
	case GET1:
		return count;
	default:
		return -1;
	}
}
void MemoryIn::run(Semaphore<std::pair<uint16_t, uint16_t>> &lock)
{
	lock.w_lock();
	read();
	lock.get().second = 0x90;
	lock.w_unlock();
}
void MemoryOut::read() { setStat(BAD); }
void MemoryOut::write()
{
	uint16_t offset = maddr & 0xfff;
	if (offset + count > 4096)
	{
		setStat(BAD);
		return;
	}
	memory[maddr >> 12].rlock();
	for (int i = 0; i < count; i++)
		putchar(memory[maddr >> 12].get()[offset + i]);
	memory[maddr >> 12].r_unlock();
	setStat(DONE);
}
void MemoryOut::set(uint64_t _value)
{
	switch (getStat())
	{
	case SET0:
		maddr = _value;
		break;
	case SET1:
		count = _value & 0xfff;
		break;
	default:
		break;
	}
}
uint64_t MemoryOut::get()
{
	switch (getStat())
	{
	case GET0:
		return maddr;
	case GET1:
		return count;
	default:
		return -1;
	}
}
void MemoryOut::run(Semaphore<std::pair<uint16_t, uint16_t>> &lock)
{
	lock.w_lock();
	write();
	lock.get().second = 0x90;
	lock.w_unlock();
}
FileIO::FileIO(const char *fname, PIOTYPE _type) : ProgramIO(_type), file(nullptr)
{
	maddr = faddr = 0;
	switch (_type)
	{
	case BLIN:
		file = fopen(fname, "rb");
		break;
	case BLOUT:
		file = fopen(fname, "wb");
		break;
	case BLIO:
		file = fopen(fname, "rb+");
		break;
	default:
		break;
	}
}
void FileIO::read()
{
	fseek(file, faddr, 0);
	memory[maddr >> 12].w_lock();
	for (int i = 0; i < 4096; i++)
		memory[maddr >> 12].get()[i] = fgetc(file);
	memory[maddr >> 12].w_unlock();
}
void FileIO::write()
{
	fseek(file, faddr, 0);
	memory[maddr >> 12].rlock();
	for (int i = 0; i < 4096; i++)
		fputc(memory[maddr >> 12].get()[i], file);
	memory[maddr >> 12].r_unlock();
}
void FileIO::set(uint64_t _addr)
{
	switch (getStat())
	{
	case SET0:
		maddr = _addr;
		break;
	case SET1:
		faddr = _addr;
		break;
	default:
		break;
	}
}
uint64_t FileIO::get()
{
	switch (getStat())
	{
	case GET0:
		return maddr;
	case GET1:
		return faddr;
	default:
		return -1;
	}
}
void FileIO::run(Semaphore<std::pair<uint16_t, uint16_t>> &lock)
{
	lock.w_lock();
	switch (getType())
	{
	case BLIN:
		read();
		break;
	case BLOUT:
		write();
		break;
	case BLIO:
		if (getStat() == READ)
			read();
		else if (getStat() == WRITE)
			write();
		else
			;
		break;
	default:
		break;
	}
	setStat(DONE);
	lock.get().second = 0x91;
	lock.w_unlock();
}
void Timer::set(uint64_t _value) { value = _value; }
uint64_t Timer::get() { return value; }
void Timer::read() { setStat(BAD); }
void Timer::write() { setStat(BAD); }
void Timer::run(Semaphore<std::pair<uint16_t, uint16_t>> &lock)
{
	lock.w_lock();
	setStat(BUSY);
	while (value--)
		;
	setStat(DONE);
	lock.get().second = 0x10;
	lock.w_unlock();
}

void IOChannel::init()
{
	mIO[0] = new SerialIn();  // using std::cin
	mIO[1] = new SerialOut(); // using std::cout
}
void IOChannel::mount(std::string name, PIOTYPE type)
{
	if (portAvail < 65536)
	{
	alloc:
		switch (type)
		{
		case SEIN:
			mIO[portAvail] = new SerialIn();
			break;
		case SEOUT:
			mIO[portAvail] = new SerialOut();
			break;
		case BLIN:
		case BLOUT:
		case BLIO:
			mIO[portAvail] = new FileIO(name.c_str(), type);
			break;
		case TIMER:
			mIO[portAvail] = new Timer();
			break;
		case MEMIN:
			mIO[portAvail] = new MemoryIn();
			break;
		case MEMOUT:
			mIO[portAvail] = new MemoryOut();
			break;
		default:
			break;
		}
		mIO[portAvail]->setStat(READY);
		portAvail += 1;
	}
	else
	{
		for (portAvail = 2; portAvail < 65536; portAvail++)

			if (mIO[portAvail] == nullptr)
				goto alloc;
	}
}
void IOChannel::umount(uint16_t port)
{
	delete mIO[port];
	mIO.erase(port);
}
void IOChannel::run()
{
	while (1)
	{
		msg->start.lock();
		if (msg->label & SHUTDOWN)
			return;
		if (mIO[msg->port] == nullptr)
			;
		else
		{
			if (msg->label & INPUT)
			{
				if (msg->label & CONTROL)
					msg->addr = mIO[msg->port]->getStat();
				else
					msg->addr = mIO[msg->port]->get();
			}
			else if (msg->label & (OUTPUT))
			{
				if (msg->label & CONTROL)
					mIO[msg->port]->setStat(PIOSTATUS(msg->addr));
				else
					mIO[msg->port]->set(msg->addr);
				if (mIO[msg->port]->getStat() & (READ | WRITE | READY))
				{
					std::thread t([this]
								  { mIO[msg->port]->run(msg->id); });
					t.detach();
				}
			}
		}
		msg->end.unlock();
	}
}